package com.mbiolance.cloud.activiti.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.mbiolance.cloud.activiti.domain.dto.ActivitiFormDto;
import com.mbiolance.cloud.activiti.domain.dto.ProcessInstanceDto;
import com.mbiolance.cloud.activiti.domain.dto.ProcessTaskDto;
import com.mbiolance.cloud.activiti.entity.ActivitiForm;
import com.mbiolance.cloud.activiti.repository.ActivitiFormRepository;
import com.mbiolance.cloud.auth.common.SysUserInfoContext;
import com.mbiolance.cloud.auth.common.SystemRuntimeException;
import com.mbiolance.cloud.auth.domain.PageModel;
import com.mbiolance.cloud.auth.domain.dto.SysUserDto;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProcessTaskService {

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ActivitiFormRepository activitiFormRepository;

    /**
     * 任务拾取
     */
    public Boolean claim(String id) {
        SysUserDto user = SysUserInfoContext.getUser();
        taskService.claim(id, String.valueOf(user.getId()));
        return true;
    }

    /**
     * 任务退回
     */
    public Boolean retTask(String id) {
        taskService.setAssignee(id, null);
        return true;
    }

    /**
     * 获取任务节点上的自定义表单
     */
    public ActivitiFormDto getForm(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String formKey = task.getFormKey();
        List<ActivitiForm> activitiForms = activitiFormRepository.findByKey(formKey);
        if (CollectionUtil.isEmpty(activitiForms)) {
            throw new SystemRuntimeException("任务表单不存在");
        }
        ActivitiForm activitiForm = activitiForms.get(0);
        return ActivitiFormDto.builder()
                .id(activitiForm.getId()).key(activitiForm.getKey())
                .contentJson(activitiForm.getContentJson())
                .build();
    }

    public PageModel<ProcessTaskDto> roleTodo(Integer number, Integer size) {
        if (number == null || number <= 0) {
            number = 1;
        }
        number--;
        SysUserDto user = Optional.ofNullable(SysUserInfoContext.getUser()).orElse(new SysUserDto());
        Integer userId = user.getId();
        if (userId == null) {
            log.info("ProcessTaskService roleTodo search user is null, please check Auth service!!!");
            return PageModel.<ProcessTaskDto>builder().build();
        }
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> tasks = taskQuery.taskCandidateUser(String.valueOf(userId)).active().includeProcessVariables()
                .orderByTaskCreateTime().desc().listPage(number * size, size);
        long count = taskQuery.count();
        List<ProcessTaskDto> dtos = new ArrayList<>();
        List<String> processInstanceIds = tasks.stream().map(Task::getProcessInstanceId).collect(Collectors.toList());
        Map<String, ProcessInstance> processInstanceMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(processInstanceIds)) {
            List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(new HashSet<>(processInstanceIds)).list();
            processInstanceMap = processInstances.stream().collect(Collectors.toMap(ProcessInstance::getId, v -> v));
        }

        for (Task task : tasks) {
            ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
            dtos.add(ProcessTaskDto.builder()
                    .id(task.getId()).name(task.getName()).assignee(task.getAssignee())
                    .processInstanceId(task.getProcessInstanceId()).processDefinitionId(task.getProcessDefinitionId())
                    .businessKey(processInstance.getBusinessKey())
                    .build());
        }
        return PageModel.<ProcessTaskDto>builder()
                .count((int) count).number(number + 1).size(size)
                .content(dtos)
                .build();
    }

    public PageModel<ProcessTaskDto> todo(Integer number, Integer size) {
        if (number == null || number <= 0) {
            number = 1;
        }
        number--;
        SysUserDto user = Optional.ofNullable(SysUserInfoContext.getUser()).orElse(new SysUserDto());
        Integer userId = user.getId();
        if (userId == null) {
            log.info("ProcessTaskService todo search user is null, please check Auth service!!!");
            return PageModel.<ProcessTaskDto>builder().build();
        }
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> tasks = taskQuery.taskAssignee(String.valueOf(userId)).active().includeProcessVariables()
                .orderByTaskCreateTime().desc().listPage(number * size, size);
        long count = taskQuery.count();
        List<ProcessTaskDto> dtos = new ArrayList<>();
        List<String> processInstanceIds = tasks.stream().map(Task::getProcessInstanceId).collect(Collectors.toList());
        Map<String, ProcessInstance> processInstanceMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(processInstanceIds)) {
            List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(new HashSet<>(processInstanceIds)).list();
            processInstanceMap = processInstances.stream().collect(Collectors.toMap(ProcessInstance::getId, v -> v));
        }
        for (Task task : tasks) {
            ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
            dtos.add(ProcessTaskDto.builder()
                    .id(task.getId()).name(task.getName()).assignee(task.getAssignee())
                    .processInstanceId(task.getProcessInstanceId()).processDefinitionId(task.getProcessDefinitionId())
                    .businessKey(processInstance.getBusinessKey())
                    .build());
        }
        return PageModel.<ProcessTaskDto>builder()
                .count((int) count).number(number + 1).size(size)
                .content(dtos)
                .build();
    }

    public PageModel<ProcessTaskDto> done(Integer number, Integer size) {
        if (number == null || number <= 0) {
            number = 1;
        }
        number--;
        SysUserDto user = Optional.ofNullable(SysUserInfoContext.getUser()).orElse(new SysUserDto());
        Integer userId = user.getId();
        if (userId == null) {
            log.info("ProcessTaskService done search user is null, please check Auth service!!!");
            return PageModel.<ProcessTaskDto>builder().build();
        }
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> tasks = taskQuery.taskAssignee(String.valueOf(userId)).includeProcessVariables()
                .orderByTaskCreateTime().desc().listPage(number * size, size);
        long count = taskQuery.count();
        List<ProcessTaskDto> dtos = new ArrayList<>();
        for (Task task : tasks) {
            dtos.add(ProcessTaskDto.builder()
                    .id(task.getId()).name(task.getName()).assignee(task.getAssignee())
                    .processInstanceId(task.getProcessInstanceId()).processDefinitionId(task.getProcessDefinitionId())
                    .build());
        }
        return PageModel.<ProcessTaskDto>builder()
                .count((int) count).number(number + 1).size(size)
                .content(dtos)
                .build();
    }

    public PageModel<ProcessTaskDto> page(Integer number, Integer size) {
        if (number == null || number <= 0) {
            number = 1;
        }
        number -= 1;
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> tasks = taskQuery.listPage(number * size, size);
        long count = taskQuery.count();
        List<ProcessTaskDto> dtos = new ArrayList<>();
        for (Task task : tasks) {
            dtos.add(ProcessTaskDto.builder()
                    .id(task.getId()).name(task.getName()).assignee(task.getAssignee())
                    .processInstanceId(task.getProcessInstanceId()).processDefinitionId(task.getProcessDefinitionId())
                    .build());
        }
        return PageModel.<ProcessTaskDto>builder()
                .count((int) count).number(number + 1).size(size)
                .content(dtos)
                .build();
    }

    /**
     * 完成任务
     * @param taskId        任务id
     * @param variables     任务变量
     */
    public void completeTask(String taskId, Map<String, Object> variables) {
        log.info("ProcessTaskService completeTask req params: {}, {}", taskId, JSON.toJSONString(variables));
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        try {
            taskService.complete(taskId, variables);
        } catch (Exception e) {
            throw new SystemRuntimeException(e.getMessage());

        }
    }

    public List<Map<String,String>> getNextBranches(String taskId) {
        List<Map<String,String>> transList = new ArrayList<Map<String,String>>();
        // 获取流程定义
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ProcessDefinitionEntity pd = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(task.getProcessDefinitionId());
        // 获取流程实例
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        // 获取当前活动的ID
        String activitiId = pi.getActivityId();
        // 获取当前活动对象
        ActivityImpl activity = pd.findActivity(activitiId);
        // 获取当前对象后续路径
        List<PvmTransition> transitions = activity.getOutgoingTransitions();
        // 遍历后续路径，封装至集合返回前台生成按钮
        Map<String,String> map = new HashMap<String,String>();
        for (PvmTransition trans : transitions) {
            String transName = (String) trans.getProperty("name");
            String transId =trans.getId();
            if(StringUtils.isNotBlank(transName)){
                map.put("key", transId);
                map.put("value", transName);
                transList.add(map);
                map = new HashMap<String,String>();
            }
        }
        if(transList.size() == 0){
            map.put("", "提交下一步");
            transList.add(map);//默认
        }
        return transList;
    }


    public InputStream image(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        // ID 为 流程定义Key
        org.activiti.bpmn.model.Process process = bpmnModel.getProcessById(processDefinition.getKey());
        // 流程节点ID
        FlowElement flowElement = process.getFlowElement(task.getTaskDefinitionKey());
        DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
        List<String> highLightedActivities = new ArrayList<>();
        highLightedActivities.add(flowElement.getId());
        InputStream inputStream = generator.generateDiagram(bpmnModel, "jpg", highLightedActivities, Collections.emptyList(), "宋体", "宋体", "宋体", null, 2.0);
        return inputStream;
    }

}
